package cn.seaboot.commons.pagination;

import cn.seaboot.commons.core.Asserts;
import cn.seaboot.commons.core.MapUtils;

import java.util.List;
import java.util.Map;

/**
 * @author Mr.css
 * @version 2023-05-08 16:54
 */
public class PageUtils {

    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();

    /**
     * 分页参数字段名
     */
    protected static final String PAGE_NUM_NAME = "page";
    /**
     * 分页参数字段名
     */
    protected static final String PAGE_SIZE_NAME = "limit";


    /**
     * 设置 Page 参数
     *
     * @param page 分页信息
     */
    protected static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }

    /**
     * 获取 Page 参数
     *
     * @return 分页结果
     */
    public static Page getLocalPage() {
        return LOCAL_PAGE.get();
    }

    /**
     * 移除本地变量
     */
    public static void clearPage() {
        LOCAL_PAGE.remove();
    }


    /**
     * 开始分页
     *
     * @param params 分页参数
     */
    public static Page startPage(Map<String, Object> params) {
        Integer pageNum = MapUtils.getInteger(params, PAGE_NUM_NAME);
        Integer pageSize = MapUtils.getInteger(params, PAGE_SIZE_NAME);
        Asserts.notNull(pageNum, "page num is undefined");
        Asserts.notNull(pageSize, "page size is undefined");
        return startPage(pageNum, pageSize);
    }


    /**
     * 开始分页
     *
     * @param pageNum  页码
     * @param pageSize 每页显示数量
     */
    public static Page startPage(int pageNum, int pageSize) {
        Asserts.state(pageNum > 0, "page num below min value: {}", pageNum);
        Asserts.state(pageSize > 0, "page size below min value: {}", pageNum);

        Page page = new Page();
        page.setPageNum(pageNum);
        page.setPageSize(pageSize);

        int start = (pageNum - 1) * pageSize;
        page.setStartRow(start);

        // 确定最后一行
        int end = start + pageSize;
        long total = page.getTotal();
        if (total != 0 && end > total) {
            end = (int) total;
        }
        if (end < page.getStartRow()) {
            end = page.getStartRow();
        }
        page.setEndRow(end);

        LOCAL_PAGE.set(page);
        return page;
    }

    /**
     * 开始分页
     *
     * @param total 数据总条数
     */
    public static void setTotal(long total) {
        Page page = LOCAL_PAGE.get();
        Asserts.notNull(page, "local page is undefined!");
        page.setTotal(total);

        // 页数
        double size = page.getPageSize();
        Asserts.state(size != 0, "page size is zero");
        int pages = (int) Math.ceil(total / size);
        page.setPages(pages);

        // 确定最后一行
        int end = page.getEndRow();
        if (total != 0 && end > total) {
            end = (int) total;
        }
        if (end < page.getStartRow()) {
            end = page.getStartRow();
        }
        page.setEndRow(end);
    }

    /**
     * 开始分页
     *
     * @param list 数据实体
     */
    public static <T> PageDetail<T> of(List<T> list) {
        Page page = LOCAL_PAGE.get();
        Asserts.notNull(page, "local page is undefined!");
        // 清除分页信息
        LOCAL_PAGE.remove();

        PageDetail<T> info = new PageDetail<>();

        info.setPageNum(page.getPageNum());
        info.setStartRow(page.getStartRow());
        info.setEndRow(page.getEndRow());

        // 总行数
        long total = page.getTotal();
        if (total == 0) {
            total = list.size();
        }
        info.setTotal(total);

        // 页数
        if (page.getPages() != 0) {
            double size = page.getPageSize();
            Asserts.state(size != 0, "page size is zero");
            int pages = (int) Math.ceil(total / size);
            page.setPages(pages);
        }

        // next & pre
        int pageNum = page.getPageNum();
        info.setPrePage(pageNum - 1);
        info.setNextPage(pageNum + 1);
        info.setHasPreviousPage(info.getPrePage() > 0);
        info.setHasNextPage(info.getNextPage() < info.getPages());

        // data & data-size
        info.setList(list);
        info.setSize(list.size());
        return info;
    }
}
